﻿using AutoMapper;
using FreeSql;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading;
using System.Threading.Tasks;
using XUCore.Extensions;
using XUCore.NetCore.FreeSql.Entity;

namespace XUCore.NetCore.FreeSql.Curd
{
    /// <summary>
    /// CURD服务
    /// </summary>
    /// <typeparam name="TKey">主键类型</typeparam>
    /// <typeparam name="TUkey">用户主键类型</typeparam>
    /// <typeparam name="TEntity">数据库实体</typeparam>
    /// <typeparam name="TDto">返回实体</typeparam>
    public abstract class CurdService<TKey, TUkey, TEntity, TDto> : ICurdService<TKey, TUkey, TEntity, TDto>
            where TEntity : EntityFull<TKey, TUkey>, new()
            where TDto : class, new()
    {
        public CurdEntity<TKey, TEntity> Curd => curd;
        /// <summary>
        /// 创建事件
        /// </summary>
        public Action<bool, TEntity> CreatedAction { get; set; }
        /// <summary>
        /// 修改事件
        /// </summary>
        public Action<bool, TEntity, TEntity> UpdatedAction { get; set; }
        /// <summary>
        /// 修改部分字段事件
        /// </summary>
        public Action<bool, IList<TKey>, Dictionary<string, object>> FieldUpdatedAction { get; set; }
        /// <summary>
        /// 删除事件
        /// </summary>
        public Action<bool, IList<TKey>> DeletedAction { get; set; }
        /// <summary>
        /// 软删除事件
        /// </summary>
        public Action<bool, IList<TKey>> SoftDeletedAction { get; set; }
        /// <summary>
        /// freesql
        /// </summary>
        public readonly IFreeSql freeSql;
        /// <summary>
        /// 仓储
        /// </summary>
        public readonly IBaseRepository<TEntity> repo;
        /// <summary>
        /// Curd
        /// </summary>
        public readonly CurdEntity<TKey, TEntity> curd;
        /// <summary>
        /// AutoMapper
        /// </summary>
        public readonly IMapper mapper;
        /// <summary>
        /// CURD服务
        /// </summary>
        /// <param name="freeSql"></param>
        /// <param name="mapper"></param>
        public CurdService(IFreeSql freeSql, IMapper mapper)
        {
            this.freeSql = freeSql;
            this.mapper = mapper;
            this.repo = freeSql.GetRepository<TEntity>();
            this.curd = new CurdEntity<TKey, TEntity>(freeSql, mapper);
        }
        /// <summary>
        /// 设置更新的列<para></para>
        ///     注意：标记 [Column(CanUpdate = false)] 的属性不会被更新
        /// </summary>
        /// <param name="id"></param>
        /// <param name="field"></param>
        /// <param name="value"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public async Task<int> UpdateFieldAsync(TKey id, string field, object value, CancellationToken cancellationToken = default)
            => await UpdateFieldAsync(new TKey[] { id }, field, value, cancellationToken);
        /// <summary>
        /// 批量设置更新的列<para></para>
        ///     注意：标记 [Column(CanUpdate = false)] 的属性不会被更新<para></para>
        /// </summary>
        /// <param name="ids"></param>
        /// <param name="field"></param>
        /// <param name="value"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public virtual async Task<int> UpdateFieldAsync(TKey[] ids, string field, object value, CancellationToken cancellationToken = default)
        {
            var keyValues = new Dictionary<string, object>
            {
                { field, value }
            };
            return await UpdateFieldAsync(ids, keyValues, cancellationToken);
        }
        /// <summary>
        /// 设置更新的列<para></para>
        ///     注意：标记 [Column(CanUpdate = false)] 的属性不会被更新<para></para>
        /// </summary>
        /// <param name="ids"></param>
        /// <param name="keyValues">var keyValues = new Dictionary&lt;string, object&gt;{ { "field1", 1 } , { "field2", "test" } };</param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public virtual async Task<int> UpdateFieldAsync(TKey[] ids, Dictionary<string, object> keyValues, CancellationToken cancellationToken = default)
        {
            var res = await curd.UpdateFieldAsync(c => ids.Contains(c.Id), keyValues, cancellationToken);

            FieldUpdatedAction?.Invoke(res > 0, ids, keyValues);

            return res;
        }
        /// <summary>
        /// 设置列的新值为基础上增加，格式：Set(a => a.Clicks + 1) 相当于 clicks=clicks+1<para></para>
        ///     指定更新，格式：Set(a => new T { Clicks = a.Clicks + 1, Time = DateTime.Now }) 相当于 set<para></para>
        ///     clicks=clicks+1,time='2019-06-19....'
        /// </summary>
        /// <param name="id"></param>
        /// <param name="column"></param>
        /// <param name="cancellationToken"></param>
        /// <typeparam name="TMember"></typeparam>
        /// <returns></returns>
        public async Task<int> UpdateFieldAsync<TMember>(TKey id, Expression<Func<TEntity, TMember>> column, CancellationToken cancellationToken = default)
            => await UpdateFieldAsync(new TKey[] { id }, column, cancellationToken);
        /// <summary>
        /// 设置列的新值为基础上增加，格式：Set(a => a.Clicks + 1) 相当于 clicks=clicks+1<para></para>
        ///     指定更新，格式：Set(a => new T { Clicks = a.Clicks + 1, Time = DateTime.Now }) 相当于 set<para></para>
        ///     clicks=clicks+1,time='2019-06-19....'
        /// </summary>
        /// <param name="ids"></param>
        /// <param name="column"></param>
        /// <param name="cancellationToken"></param>
        /// <typeparam name="TMember"></typeparam>
        /// <returns></returns>
        public async Task<int> UpdateFieldAsync<TMember>(TKey[] ids, Expression<Func<TEntity, TMember>> column, CancellationToken cancellationToken = default)
            => await curd.UpdateFieldAsync(ids, column, cancellationToken);
        /// <summary>
        /// 物理删除数据
        /// </summary>
        /// <param name="id"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public async Task<int> DeleteAsync(TKey id, CancellationToken cancellationToken = default)
            => await DeleteAsync(new TKey[] { id }, cancellationToken);
        /// <summary>
        /// 批量物理删除数据
        /// </summary>
        /// <param name="ids"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public virtual async Task<int> DeleteAsync(TKey[] ids, CancellationToken cancellationToken = default)
        {
            var res = await curd.DeleteAsync(ids, cancellationToken);

            DeletedAction?.Invoke(res > 0, ids);

            return res;
        }
        /// <summary>
        /// 逻辑删除
        /// </summary>
        /// <param name="id"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public async Task<int> SoftDeleteAsync(TKey id, CancellationToken cancellationToken = default)
            => await SoftDeleteAsync(new TKey[] { id }, cancellationToken);
        /// <summary>
        /// 批量逻辑删除
        /// </summary>
        /// <param name="ids"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public virtual async Task<int> SoftDeleteAsync(TKey[] ids, CancellationToken cancellationToken = default)
        {
            var res = await curd.UpdateFieldAsync(ids, nameof(EntityFull<TKey, TUkey>.IsDeleted), true, cancellationToken);

            SoftDeletedAction?.Invoke(res > 0, ids);

            return res;
        }
        /// <summary>
        /// 根据id获取一条记录
        /// </summary>
        /// <param name="id"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public virtual async Task<TDto> GetAsync(TKey id, CancellationToken cancellationToken = default)
            => await curd.GetAsync<TDto>(id, cancellationToken);
        /// <summary>
        /// 根据id获取一条记录
        /// </summary>
        /// <param name="id"></param>
        /// <param name="select">指定返回的数据字段信息</param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public virtual async Task<TReturn> GetAsync<TReturn>(TKey id, Expression<Func<TEntity, TReturn>> select, CancellationToken cancellationToken = default)
            => await curd.GetAsync(id, select, cancellationToken);
        /// <summary>
        /// 查询是否存在
        /// </summary>
        /// <param name="exp"></param>
        /// <returns></returns>
        public bool Any(Expression<Func<TEntity, bool>> exp)
            => curd.Any(exp);
        /// <summary>
        /// 查询是否存在
        /// </summary>
        /// <param name="exp"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public async Task<bool> AnyAsync(Expression<Func<TEntity, bool>> exp, CancellationToken cancellationToken)
            => await curd.AnyAsync(exp, cancellationToken);
        /// <summary>
        /// 查询数据库总记录数
        /// </summary>
        /// <param name="exp"></param>
        /// <returns></returns>
        public long Count(Expression<Func<TEntity, bool>> exp)
            => curd.Count(exp);
        /// <summary>
        /// 查询数据库总记录数
        /// </summary>
        /// <param name="exp"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public async Task<long> CountAsync(Expression<Func<TEntity, bool>> exp, CancellationToken cancellationToken)
            => await curd.CountAsync(exp, cancellationToken);
    }
}
